package com.hm.pan.controller;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.hm.pan.model.FileInfo;
import com.hm.pan.model.ResultObj;
import com.hm.pan.model.UserFile;
import com.hm.pan.service.FileInfoService;
import com.hm.pan.service.UserFileService;
import com.hm.pan.util.DownloadUtil;
import com.hm.pan.util.DownloadUtil.ZipFileEntry;
import com.hm.pan.util.StringUtil;
import com.hm.pan.util.UserFileUtil;

/**
 * 文件下载模块
 */
@Controller
@RequestMapping("userfile")
public class DownloadController {

	@Resource
	private UserFileService userFileService;
	@Resource
	private FileInfoService fileInfoService;

	// 临时文件夹
	private String temp_dir = "download_tmp/";

	@GetMapping("download")
	@ResponseBody
	public void download(@RequestHeader Map<String, String> headers, HttpServletResponse response,
			@RequestParam("userfileId") long[] userfileIds) {
		if (userfileIds.length == 0) {
			try {
				ResultObj resultObj = new ResultObj();
				resultObj.setStatusCode(400);
				resultObj.setMessage("userfileId cannot be empty");
				response.getWriter().print(JSON.toJSONString(resultObj));;
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}

		if (headers == null) {
			headers = new HashMap<>();
		}

		// 文件名
		String filename = null;
		// 下载的文件对象
		File file = null;

		createTmpDir();

		if (userfileIds.length == 1) {
			UserFile filter = new UserFile();
			filter.setUserfileId(userfileIds[0]);
			filter.setIsDelete(false);
			try {
				UserFile userFile = userFileService.getOneFile(filter);

				if (userFile == null) {
					ResultObj resultObj = new ResultObj();
					resultObj.setStatusCode(404);
					resultObj.setMessage("file not found");
					response.getWriter().print(JSON.toJSONString(resultObj));
					return;
				}

				if (userFile.getFileType()) { // =========================== 单个文件下载 ================================
					filename = userFile.getFileName();
					FileInfo filter2 = new FileInfo();
					filter2.setFileId(userFile.getFileId());
					file = fileInfoService.getFile(filter2);
				} else { // ========================== 单个文件夹下载 ===============================
					filename = userFile.getFileName() + ".zip";

					// 临时文件名
					String tempFile = null;
					List<UserFile> deepGetAllFiles = userFileService.deepGetAllFiles(userfileIds[0]);
					List<ZipFileEntry> list = UserFileUtil.prepareZip(deepGetAllFiles, userfileIds[0]);

					tempFile = temp_dir + StringUtil.MD5(JSON.toJSONString(deepGetAllFiles)) + ".zip";
					file = new File(tempFile);
					if (!file.exists()) {
						synchronized (DownloadController.class) {
							DownloadUtil.compress(list, tempFile);
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				try {
					// 出错删除临时文件
					if (file != null) {
						file.delete();
					}
					response.sendError(500);
				} catch (Exception x) {
				}

				return;
			}
		} else { // =============================== 多个文件下载 ==================================
			filename = "download.zip";

			try {
				List<UserFile> list = new ArrayList<>();
				for (long l : userfileIds) {
					List<UserFile> deepGetAllFiles = userFileService.deepGetAllFiles(l);
					for (UserFile userFile : deepGetAllFiles) {
						list.add(userFile);
					}
				}
				String tempFile = null;
				// 转为ZipFileEntry
				List<ZipFileEntry> entries = UserFileUtil.prepareZip(list, 69);
				// 生成临时文件名
				tempFile = temp_dir + StringUtil.MD5(JSON.toJSONString(list)) + ".zip";
				// 打包
				file = new File(tempFile);
				if (!file.exists()) {
					synchronized (DownloadController.class) {
						DownloadUtil.compress(entries, tempFile);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				try {
					// 出错删除临时文件
					if (file != null) {
						file.delete();
					}
					response.sendError(500);
				} catch (Exception x) {
				}
				return;
			}

//			file = new File(filename);
		}

		long fileSize;
		if (!file.isFile() || !file.canRead() || (fileSize = file.length()) == 0) {
			ResultObj resultObj = new ResultObj();
			resultObj.setStatusCode(404);
			resultObj.setMessage("服务器找不到指定资源");
			try {
				response.getWriter().print(JSON.toJSONString(resultObj));
			} catch (IOException e) {
			}
			return;
		}

		// 浏览器请求的范围是否有效
		boolean valid = true;
		// 文件读取范围
		Long[] ranges = new Long[2];

		// 取浏览器发过来的请求头
		String range = null;
		for (String key : headers.keySet()) {
			if ("range".equalsIgnoreCase(key)) {
				range = headers.get(key);
				break;
			}
		}
		// 未发送指定请求头或格式不正确，pass
		if (StringUtil.isEmpty(range) || !range.matches("^bytes=\\d+-\\d*$")) {
			valid = false;
		} else {
			System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>客户端请求范围：" + range);
			// 取出读取范围
			String[] split = range.substring(6).split("-");
			// 开始读取位置
			ranges[0] = Long.valueOf(split[0]);
			// 结束读取位置
			if (split.length > 1 && !StringUtil.isEmpty(split[1])) {
				ranges[1] = Long.valueOf(split[1]);

				if (ranges[0] > ranges[1] || ranges[1] >= fileSize) {
					valid = false;
				}
			} else {
				ranges[1] = fileSize - 1;
			}
		}

		if (valid) { // 范围有效
			// HTTP/1.1 206 Partial Content
			response.setStatus(206);
			response.setContentLengthLong(ranges[1] - ranges[0] + 1);
			response.setHeader("Content-Range", "bytes " + ranges[0] + "-" + ranges[1] + "/" + fileSize);
		} else {
			// HTTP/1.1 200 OK
			ranges[0] = 0L;
			ranges[1] = fileSize - 1;
			response.setStatus(200);
			response.setContentLengthLong(fileSize);
		}

		System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>文件名：" + filename);
		String encodedFilename = DownloadUtil.getDownloadFileName(filename, headers.get("User-Agent"));
		// 告诉浏览器下载这个文件，而不是直接打开显示
		response.setHeader("Content-Disposition", "attachment;charset=UTF-8;filename=\"" + encodedFilename + "\"");
		response.setContentType("application/octet-stream");
		// 告诉浏览器服务器支持部分下载（断点续传）
		response.setHeader("Accept-Ranges", "bytes");
		response.setHeader("Cache-Control", "no-cache");

		System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 开始读取数据：" + ranges[0] + "-" + ranges[1]);

		try (OutputStream os = response.getOutputStream(); RandomAccessFile raf = new RandomAccessFile(file, "r")) {
			// 缓冲大小10KB
			byte[] buffer = new byte[10240];
			long times = fileSize / buffer.length;

			raf.seek(ranges[0]);
			for (int i = 0; i < times; i++) {
				raf.read(buffer, 0, buffer.length);
				os.write(buffer);
				os.flush();
			}

			int read = raf.read(buffer, 0, buffer.length);
			if (read != -1) {
				os.write(buffer, 0, read);
				os.flush();
			}
		} catch (Exception e) {
			if (e instanceof org.apache.catalina.connector.ClientAbortException) {
				System.err.println("客户端断开了连接");
			} else {
				e.printStackTrace();
			}
		}

	}

	/**
	 * 创建临时目录
	 */
	public boolean createTmpDir() {
		File file = new File(temp_dir);

		if (!file.exists()) {
			return file.mkdir();
		}

		return true;
	}
}
